/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package bllRoom;

import bllInterface.Account;
import bllInterface.BLLChatServer;
import bllInterface.BaseChatUser;
import bllInterface.ChatMessage;
import bllInterface.ChatMessageContent;
import bllInterface.IRemoteBufferedOutputStream;
import bllInterface.RemoteBufferedOutputStream;
import bllInterface.iChatUser;
import bllInterface.iRoom;
import bllInterface.iFriendListener;
import bllobject.AccountImpl;
import dalfactory.SDALFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import utils.DataRow;
import utils.DataTable;


public class RoomImpl extends BaseChatUser implements iRoom
{
    private String roomID;
    private String roomName;
    
    final private Collection<String> userNames = new HashSet();
    private ArrayList<iFriendListener> listeners = new ArrayList<>();
    private ArrayList<Account> accs = new ArrayList<>();
    
    private ByteArrayInputStream input;
    private IRemoteBufferedOutputStream output = null;
    
    private BLLChatServer cs;
    
    public RoomImpl(int id, String roomName, BLLChatServer chatSv) throws RemoteException
    {
        super();
        roomID = String.valueOf(id);
        this.roomName = roomName;
        this.cs = chatSv;
    }
    
    @Override
    public void onHasMessage(ChatMessage msg) throws RemoteException
    {
        final ChatMessage dispatchMsg = ChatMessage.createDispatchMessage(roomID, msg.getSender(),(String) msg.getContent().get(ChatMessageContent.MESSAGE_CONTENT), msg.getReceiver());
        dispatchMsg.setIsFile(msg.isFile());
        dispatchMsg.getContent().put(ChatMessageContent.ORIGINAL_SENDER, msg.getSender());
        
        for (final String user : userNames)
        {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    try {
                        ChatMessage cloneDispatch;
                        synchronized (dispatchMsg)
                        {
                            cloneDispatch = dispatchMsg.clone();
                        }
                        
                        cloneDispatch.setReceiver(user);
                        cs.post(cloneDispatch);
                    } catch (RemoteException ex) {
                        Logger.getLogger(RoomImpl.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }).start();
        }
    }

    @Override
    public String getChatID() throws RemoteException
    {
        return roomID;
    }

    @Override
    public String getName() throws RemoteException
    {
        return roomName;
    }
    
    @Override
    public boolean join(iChatUser user) throws RemoteException
    {
        synchronized(this.userNames)
        {
            if (!this.userNames.contains(user.getChatID()))
            {
                DataTable dt = SDALFactory.getInstance().getAccount().getAccountInfo(user.getChatID());
                if(dt != null && dt.size() != 0)
                {
                    DataRow row = dt.get(0);
                    
                    Account acc = AccountImpl.createRemoteAccount(row.get("UserID"), row.get("UserName"), Long.parseLong(row.get("Experience")),
                                                    Integer.parseInt(row.get("Level")), row.get("Type"));
                    notifyAll(acc);
                    user.addListener(this);
                    
                    this.userNames.add(user.getChatID());
                    this.accs.add(acc);
                    return true;
                }
            }
        }
        
        return false;
    }

    @Override
    public boolean leave(iChatUser user) throws RemoteException{
        synchronized(this.userNames)
        {
            if (this.userNames.contains(user.getChatID()))
            {
                user.removeListener(this);
                this.userNames.remove(user.getChatID());
                
                for(int i = 0; i < accs.size(); ++i){
                    if(accs.get(i).getUserID().equals(user.getChatID()))
                        accs.remove(i);
                }
                
                if( user.isBoss( roomID ) && accs.size() > 0 )
                {
                    int rand = (int)(Math.random() * accs.size());
                    cs.changeKey(accs.get(rand).getUserID(), roomID);
                }
                
                return true;
            }
        }
        
        return false;
    }
    
    @Override
    public void onChatUserStatusChanged(iChatUser chatUser) throws RemoteException
    {
    }

    @Override
    public ArrayList<ChatMessage> getHistory() throws RemoteException 
    {
        ArrayList<ChatMessage> messages = new ArrayList<>();
        DataTable dt = SDALFactory.getInstance().getMessage().getAllMessageTo(roomID);
        if( dt != null )
        {
            for(int i = 0; i < dt.size(); ++i)
            {
                DataRow row = dt.get(i);
                ChatMessage cm = ChatMessage.createMessage(row.get("Sender"), row.get("Content"), row.get("Receiver"));
                messages.add(cm);
            }
        }
        return messages;
    }

    private void notifyAll(Account acc) throws RemoteException
    {
        for(int i = 0; i < listeners.size(); ++i)
        {
            listeners.get(i).add(acc);
        }
    }

    @Override
    public void addFriendListener(iFriendListener listener) throws RemoteException 
    {
        for(Account acc : accs)
        {
            listener.add(acc);
        }
        
        listeners.add(listener);
    }

    @Override
    public void removeFriendListener(iFriendListener listener) throws RemoteException 
    {
        listeners.remove(listener);
    }

    @Override
    public ByteArrayInputStream getInputStream() throws RemoteException 
    {
        return input;
    }

    @Override
    public IRemoteBufferedOutputStream getOutputStream() throws RemoteException 
    {
        if( output == null )
            output = RemoteBufferedOutputStream.CreateUnicastBufferedOutputStream();
        return output;
    }

    @Override
    public void setInputStream(InputStream stream) throws RemoteException 
    {
        this.input  =   (ByteArrayInputStream)stream;
    }

    @Override
    public void setOutputStream(RemoteBufferedOutputStream stream) throws RemoteException 
    {
        this.output = (RemoteBufferedOutputStream)stream;
    }

    @Override
    public void onHasVoice(final String sender, String receiver, final byte[] pack) throws RemoteException {
        
        for (final String user : userNames)
        {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    try {
                        cs.postVoice(roomID, user, pack);
                    } catch (RemoteException ex) {
                        Logger.getLogger(RoomImpl.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }).start();
        }
    }

    @Override
    public void onHasKey(String sender) throws RemoteException {
    }

    @Override
    public boolean isBoss( String receiver ) throws RemoteException {
        return false;
    }

    @Override
    public void setBoss( String sender, boolean isBoss ) throws RemoteException {
    }
}
